﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ForumsBrowser.ServiceContracts;
using ForumsBrowser.Common;
using ForumsBrowser.DataObjects;
using System.Net;
using System.Xml.Serialization;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Globalization;
using System.Web;

namespace ForumsBrowser.SiteAccess
{
    public class SiteAccessService : IForumsBrowserService
    {
        private T GetData<T>(string url) where T : class
        {
            T result = null;

            WebRequest req = HttpWebRequest.Create(url);
            try
            {
                req.Timeout = Configuration.WebRequestTimeout;
                WebResponse response = req.GetResponse();
                XmlSerializer ser = new XmlSerializer(typeof(T));
                result = (T)ser.Deserialize(response.GetResponseStream());
            }
            catch (Exception ex)
            {
                ErrorHandler.HandleError(ex);
            }
            return result;
        }

        public ThreadData GetThread(string url)
        {
            Debug.Write("GetThread");
            
            if (!url.EndsWith("?outputas=xml"))
                url = url + "?outputas=xml";

            var result = GetData<ThreadData>(url);
            if (result != null)
            {
                result.Threads.Topic = HttpUtility.HtmlDecode(result.Threads.Topic);
                foreach (var msg in result.Messages)
                    msg.Subject = HttpUtility.HtmlDecode(msg.Subject);
            }
            return result;
        }

        public ThreadData GetThread(string forumName, Guid id)
        {
            Debug.Write("GetThread");
            string url = Configuration.GetThreadUrl(forumName, id);
            ThreadData result = GetThread(url);
            if (result != null)
                result.Threads.Topic = HttpUtility.HtmlDecode(result.Threads.Topic);
            return result;
        }

        public ForumData GetPosts(string forumName, DateTime? since)
        {
            Debug.Write("GetPosts");
            string url = Configuration.GetForumUrl(forumName, since);
            var result = GetData<ForumData>(url);

            if (result != null)
            {
                foreach (Thread thread in result.Threads)
                    thread.Topic = HttpUtility.HtmlDecode(thread.Topic);

                foreach (Message msg in result.Messages)
                    msg.Subject = HttpUtility.HtmlDecode(msg.Subject);
            }
            return result;
        }

        private static object m_lock = new object();

        public AvailableForumData GetAvailableForums(bool forceRefresh)
        {
            AvailableForumData result = Persister.GetData<AvailableForumData>("availableForums");

            lock (m_lock)
            {
                try
                {

                    if (result == null || forceRefresh)
                    {
                        result = new AvailableForumData();
                        string url = Configuration.AvailableForumsUrl;
                        WebRequest request = HttpWebRequest.Create(url);
                        WebResponse response = request.GetResponse();
                        string categoryRegex = "<li class=\"category box\"><h2><a.+?>(.+?)<";
                        using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                        {
                            string text = sr.ReadToEnd();
                            foreach (var m in Regex.Matches(text, categoryRegex, RegexOptions.Singleline).OfType<Match>())
                            {
                                AvailableForumCategory category = new AvailableForumCategory { CategoryName = HttpUtility.HtmlDecode(m.Groups[1].Value) };
                                string block = Regex.Escape(m.Value);
                                string inner = Regex.Match(text, block + "(.+?)(category box|$)", RegexOptions.Singleline).Groups[1].Value;


                                foreach (var f in Regex.Matches(inner, "<li class=\"forum\".+?.+?<a href=\"(.+?)\">(.+?)<.+?</li>", RegexOptions.Singleline).OfType<Match>())
                                {
                                    AvailableForum forum = new AvailableForum();
                                    forum.ForumLongName = HttpUtility.HtmlDecode(f.Groups[2].Value);
                                    forum.ForumShortName = Regex.Match(f.Groups[1].Value, "http://social.msdn.microsoft.com/Forums/.+?/(.+?)/", RegexOptions.Singleline).Groups[1].Value;
                                    category.AvailableForums.Add(forum);
                                }

                                result.Categories.Add(category);
                            }
                        }
                        SaveAvailableForums(result);
                    }
                }
                catch
                {
                    return null;
                }
            }

            return result;
            
        }

        private void SaveAvailableForums(AvailableForumData availableForums)
        {
            Persister.SaveData(availableForums, "availableForums");
        }

        private static Dictionary<string, Image> m_imageCache = new Dictionary<string, Image>();

        public Image GetImage(string url)
        { 
            if (!m_imageCache.ContainsKey(url))
            {
                Debug.WriteLine("Getting an image...");
                WebRequest req = HttpWebRequest.Create(url);
                try
                {
                    req.Timeout = 3000;
                    WebResponse response = req.GetResponse();
                    m_imageCache[url] = Image.FromStream(response.GetResponseStream());
                }
                catch (Exception ex)
                {
                    ErrorHandler.HandleError(ex);
                    return null;
                }
            }
            else
            {
                Debug.WriteLine("Found image in cache...");
            }
            return m_imageCache[url];
        }

        private void SetSelected(string forumShortName, bool selected)
        {
            var available = GetAvailableForums(false);
            var forum = available.Categories.SelectMany(c => c.AvailableForums).FirstOrDefault(f => f.ForumShortName == forumShortName);
            if (forum != null)
                forum.IsSelected = selected;
            SaveAvailableForums(available);  
        }

        public void AddSelectedForum(string forumShortName)
        {
            SetSelected(forumShortName, true);
        }

        public void RemoveSelectedForum(string forumShortName)
        {
            SetSelected(forumShortName, false);
        }
    }
}
